﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Reactive.Subjects;
using System.Threading.Tasks;

using iTool.Cloud.Database.DistributedFilePlacementStrategyFixedProvider;
using iTool.ClusterComponent;

using Lucene.Net.Sandbox.Queries;

using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.Data.Sqlite;
using Orleans;
using Orleans.Runtime;

using static System.Net.Mime.MediaTypeNames;

namespace iTool.Cloud.Database.ServiceProvider
{
    /// <summary>
    /// 1 文件库
    /// 2 文件碎片库 => 文件库
    /// </summary>
    [DistributedFilePlacementStrategy]
    public class FileService : iToolServiceBase, iFileService
    {
        readonly ISiloStatusOracle siloStatusOracle;
        const string DirectoryPath = "./Upload";
        const string DataSource = DirectoryPath + "/Upload.files";
        const string DataPieceSource = DirectoryPath + "/Upload.pieces";
        string connectionString = string.Empty;
        string connectionStringOfPiece = string.Empty;
        string FileID { get; set; }
        bool isHasRoute { get; set; }
        //Dictionary<string, MemoryStream> keyValuePairs

        UploadInfo FileInfo { get; set; }

        public FileService(ISiloStatusOracle siloStatusOracle)
        {
            if (!Directory.Exists(DirectoryPath))
                Directory.CreateDirectory(DirectoryPath);
            this.siloStatusOracle = siloStatusOracle;
        }

        public async override Task OnActivateAsync()
        {
            this.FileID = this.GetPrimaryKeyString();
            if (this.FileID.IndexOf('/') > -1)
            {
                this.isHasRoute = true;
                this.FileID = this.FileID.Split('/')[1];
            }

            this.connectionString = new SqliteConnectionStringBuilder
            {
                DataSource = DataSource,
                Mode = SqliteOpenMode.ReadWriteCreate,
                Cache = SqliteCacheMode.Shared,
                Pooling = true
            }.ToString();

            this.connectionStringOfPiece = new SqliteConnectionStringBuilder
            {
                DataSource = DataPieceSource,
                Mode = SqliteOpenMode.ReadWriteCreate,
                Cache = SqliteCacheMode.Shared,
                Pooling = true
            }.ToString();

            // UploadState 0 创建， 1 上传中， 200 已完成在本库， 201 已完成在分片库
            string[] sqls1 = new string[] {
                $"CREATE TABLE IF NOT EXISTS FileList(key varchar PRIMARY KEY, User varchar, Role varchar, SuffixName any,ContentType any,UploadState int, TotalLength long, FileStream any,CreateDate any)"
            };

            string[] sqls2 = new string[] {
                $"CREATE TABLE IF NOT EXISTS FilePieceList(key varchar, Number int,IsEndNUmber int,PieceLength long,FileStream any,CreateDate any)"
            };

            //command.CommandText = $"CREATE INDEX FilePieceList_key on FilePieceList(key)";
            //SELECT 1 FROM sqlite_master WHERE type = 'index' and name = 'FilePieceList_key';


            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                foreach (var sql in sqls1)
                {
                    using (SqliteCommand command = new SqliteCommand(sql, connection))
                    {
                        command.ExecuteNonQuery();
                    }
                }
            }

            using (var connection = new SqliteConnection(this.connectionStringOfPiece))
            {
                connection.Open();
                foreach (var sql in sqls2)
                {
                    using (SqliteCommand command = new SqliteCommand(sql, connection))
                    {
                        command.ExecuteNonQuery();
                        command.CommandText = "SELECT 1 FROM sqlite_master WHERE type = 'index' and name = 'FilePieceList_key'";
                        var reader = command.ExecuteReader();
                        if (!reader.HasRows)
                        {
                            reader.Close();
                            await reader.DisposeAsync();
                            command.CommandText = "CREATE INDEX FilePieceList_key on FilePieceList(key)";
                            command.ExecuteNonQuery();
                        }
                    }
                }
            }

            await this.GetFileInfoAsync();

            await base.OnActivateAsync();
        }

        public async Task<UploadInfo> GetFileInfoAsync()
        {
            if (this.isHasRoute && this.FileInfo.UploadState < 200)
            {
                this.FileInfo = new UploadInfo 
                {
                    UploadState = 0,
                    TotalLength = 0,
                    SuffixName = null
                };
            }

            if (this.FileInfo.UploadState > 0)
            {
                return new UploadInfo 
                {
                    CreateDate= this.FileInfo.CreateDate,
                    //FileStream= this.FileInfo.FileStream,
                    UploadState= this.FileInfo.UploadState,
                    TotalLength= this.FileInfo.TotalLength,
                    Role= this.FileInfo.Role,
                    SuffixName= this.FileInfo.SuffixName,
                    User = this.FileInfo.User,
                    ContentType = this.FileInfo.ContentType,
                };
            }

            if (this.FileInfo.TotalLength > 0 || !string.IsNullOrEmpty(this.FileInfo.SuffixName))
            {
                return this.FileInfo;
            }

            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"select User,Role,SuffixName,TotalLength,CreateDate,FileStream,UploadState,ContentType from FileList where key=$key";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);
                var reader = selectCommand.ExecuteReader();
                while (reader.Read()) 
                {
                    var outputStream = new MemoryStream();
                    using (var readStream = reader.GetStream(5))
                    {
                        await readStream.CopyToAsync(outputStream);
                    }

                    this.FileInfo = new UploadInfo
                    {
                        User = reader.GetString(0),
                        Role = reader.GetString(1),
                        SuffixName = reader.GetString(2),
                        TotalLength = reader.GetInt32(3),
                        CreateDate = DateTime.Parse(reader.GetString(4)),
                        FileStream = outputStream.ToArray(),
                        UploadState = reader.GetInt32(6),
                        ContentType = reader.GetString(7),
                    };

                    return new UploadInfo
                    {
                        CreateDate = this.FileInfo.CreateDate,
                        //FileStream= this.FileInfo.FileStream,
                        UploadState = this.FileInfo.UploadState,
                        TotalLength = this.FileInfo.TotalLength,
                        Role = this.FileInfo.Role,
                        SuffixName = this.FileInfo.SuffixName,
                        User = this.FileInfo.User,
                        ContentType = this.FileInfo.ContentType,
                    };
                }
            }
            return default;
        }

        public async Task<UploadPiece> GetStreamAsync(int lastNumber = 1)
        {
            if (this.FileInfo.FileStream?.Length > 0)
            {
                return new UploadPiece
                {
                    FileStream = this.FileInfo.FileStream,
                    IsEndNUmber = true,
                    Number = lastNumber
                };
            }

            // 获取最后一片，用于断点上传比较
            if (lastNumber == -1) 
            {
                using (var connectionOfPiece = new SqliteConnection(this.connectionStringOfPiece))
                {
                    connectionOfPiece.Open();
                    var selectCommandOfPiece = connectionOfPiece.CreateCommand();
                    selectCommandOfPiece.CommandText = @"select IsEndNUmber,FileStream from FilePieceList where key=$key order by Number desc limit 1";
                    selectCommandOfPiece.Parameters.AddWithValue("$key", this.FileID);
                    var readerOfPiece = selectCommandOfPiece.ExecuteReader();
                    while (readerOfPiece.Read())
                    {
                        var outputStream = new MemoryStream();
                        using (var readStream = readerOfPiece.GetStream(1))
                        {
                            await readStream.CopyToAsync(outputStream);
                        }

                        return new UploadPiece
                        {
                            FileStream = outputStream.ToArray(),
                            IsEndNUmber = readerOfPiece.GetInt16(0) == 1,
                            Number = lastNumber
                        };
                    }
                }

                return default;
            }

            if (lastNumber > 1)
            {
                using (var connectionOfPiece = new SqliteConnection(this.connectionStringOfPiece))
                {
                    connectionOfPiece.Open();
                    var selectCommandOfPiece = connectionOfPiece.CreateCommand();
                    selectCommandOfPiece.CommandText = @"select IsEndNUmber,FileStream from FilePieceList where key=$key and Number=$num";
                    selectCommandOfPiece.Parameters.AddWithValue("$key", this.FileID);
                    selectCommandOfPiece.Parameters.AddWithValue("$num", lastNumber);
                    var readerOfPiece = selectCommandOfPiece.ExecuteReader();
                    while (readerOfPiece.Read())
                    {
                        var outputStream = new MemoryStream();
                        using (var readStream = readerOfPiece.GetStream(1))
                        {
                            await readStream.CopyToAsync(outputStream);
                        }

                        return new UploadPiece
                        {
                            FileStream = outputStream.ToArray(),
                            IsEndNUmber = readerOfPiece.GetInt16(0) == 1,
                            Number = lastNumber
                        };
                    }
                }

                return default;
            }

            using (var connection = new SqliteConnection(this.connectionString)) 
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"select User,Role,SuffixName,TotalLength,CreateDate,FileStream,UploadState,ContentType from FileList where key=$key";
                //selectCommand.CommandText = @"select UploadState,FileStream from FileList where key=$key";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);
                var reader = selectCommand.ExecuteReader();
                while (reader.Read())
                {
                    int state = reader.GetInt32(6);
                    if (state == 200)
                    {
                        var outputStream = new MemoryStream();
                        using (var readStream = reader.GetStream(5))
                        {
                            await readStream.CopyToAsync(outputStream);
                        }

                        this.FileInfo = new UploadInfo
                        {
                            User = reader.GetString(0),
                            Role = reader.GetString(1),
                            SuffixName = reader.GetString(2),
                            TotalLength = reader.GetInt32(3),
                            CreateDate = DateTime.Parse(reader.GetString(4)),
                            FileStream = outputStream.ToArray(),
                            UploadState = state,
                            ContentType = reader.GetString(7),
                        };

                        return new UploadPiece
                        {
                            FileStream = this.FileInfo.FileStream,
                            IsEndNUmber = true,
                            Number = lastNumber
                        };
                    }
                    else if (state == 201)
                    {
                        using (var connectionOfPiece = new SqliteConnection(this.connectionStringOfPiece))
                        {
                            connectionOfPiece.Open();
                            var selectCommandOfPiece = connectionOfPiece.CreateCommand();
                            selectCommandOfPiece.CommandText = @"select IsEndNUmber,FileStream from FilePieceList where key=$key and Number=$num";
                            selectCommandOfPiece.Parameters.AddWithValue("$key", this.FileID);
                            selectCommandOfPiece.Parameters.AddWithValue("$num", lastNumber);
                            var readerOfPiece = selectCommandOfPiece.ExecuteReader();
                            while (readerOfPiece.Read())
                            {
                                var outputStream = new MemoryStream();
                                using (var readStream = readerOfPiece.GetStream(1))
                                {
                                    await readStream.CopyToAsync(outputStream);
                                }

                                return new UploadPiece
                                {
                                    FileStream = outputStream.ToArray(),
                                    IsEndNUmber = readerOfPiece.GetInt16(0) == 1,
                                    Number = lastNumber
                                };
                            }
                        }
                    }
                }
            }

            return default;
        }

        public async Task UploadAsync(UploadInfo uploadInfo)
        {
            if (this.FileInfo.UploadState > 0)
            {
                if (this.FileInfo.UploadState < 200)
                {
                    // 失败重传，这里非断点续传
                    using (var connection = new SqliteConnection(this.connectionString))
                    {
                        connection.Open();
                        var command = connection.CreateCommand();
                        command.CommandText = @"DELETE FROM FileList where key=$key";
                        command.Parameters.AddWithValue("$key", this.FileID);
                        command.ExecuteNonQuery();
                    }

                    using (var connectionOfPiece = new SqliteConnection(this.connectionStringOfPiece))
                    {
                        connectionOfPiece.Open();
                        var selectCommandOfPiece = connectionOfPiece.CreateCommand();
                        selectCommandOfPiece.CommandText = @"DELETE FROM FilePieceList where key=$key";
                        selectCommandOfPiece.Parameters.AddWithValue("$key", this.FileID);
                        selectCommandOfPiece.ExecuteNonQuery();
                    }
                    this.FileInfo = default;
                }
                else
                {
                    return;
                }
            }

            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var insertCommand = connection.CreateCommand();
                insertCommand.CommandText =
                @"
                    INSERT INTO FileList(key, User,Role, SuffixName, UploadState, TotalLength, FileStream,CreateDate,ContentType)
                    VALUES ($key, $User,$Role, $SuffixName, $UploadState, $TotalLength,$FileStream,$CreateDate,$ContentType);
                ";

                int state = uploadInfo.TotalLength == uploadInfo.FileStream?.Length ? 200 : uploadInfo.FileStream?.Length > 0 ? 1 : 0;
                if (uploadInfo.TotalLength == 0 && uploadInfo.FileStream?.Length > 0)
                {
                    state = 200;
                }
                else if (uploadInfo.TotalLength == uploadInfo.FileStream?.Length && uploadInfo.TotalLength == 0)
                {
                    state = 0;
                }

                insertCommand.Parameters.AddWithValue("$key", this.FileID);
                insertCommand.Parameters.AddWithValue("$User", uploadInfo.User ?? string.Empty);
                insertCommand.Parameters.AddWithValue("$Role", uploadInfo.Role ?? string.Empty);
                insertCommand.Parameters.AddWithValue("$SuffixName", uploadInfo.SuffixName ?? string.Empty);
                insertCommand.Parameters.AddWithValue("$UploadState", state == 200 ? state : 1);
                insertCommand.Parameters.AddWithValue("$TotalLength", state == 200 ? uploadInfo.FileStream?.LongLength : 0);
                insertCommand.Parameters.AddWithValue("$CreateDate", DateTime.Now);
                insertCommand.Parameters.AddWithValue("$ContentType", uploadInfo.ContentType ?? string.Empty);
                insertCommand.Parameters.AddWithValue("$FileStream", uploadInfo.FileStream ?? new byte[0]);
                insertCommand.ExecuteNonQuery();

                if (state == 200 || state == 0)
                {

                    //if (state == 200)
                    //{
                    //    using (var writeStream = new SqliteBlob(connection, "FileList", "FileStream", rowid))
                    //    {
                    //        new MemoryStream(uploadInfo.FileStream).CopyTo(writeStream);
                    //    }
                    //}

                    if (uploadInfo.TotalLength == 0 && uploadInfo.FileStream!= null)
                    {
                        uploadInfo.TotalLength = uploadInfo.FileStream.Length;
                    }

                    this.FileInfo = new UploadInfo
                    {
                        User = uploadInfo.User,
                        Role = uploadInfo.Role,
                        FileStream = uploadInfo.FileStream,
                        CreateDate = uploadInfo.CreateDate,
                        SuffixName = uploadInfo.SuffixName,
                        TotalLength = uploadInfo.TotalLength,
                        UploadState = state == 200 ? state : 1,
                        ContentType = uploadInfo.ContentType,
                    };

                    if (this.FileInfo.UploadState == 200)
                    {
                        // 同步通知
                        await this.NotifySyncFileDataAsync();
                    }
                    return;
                }
                else if (state == 1)
                {
                    await this.UploadPieceAsync(new UploadPiece
                    {
                        FileStream = uploadInfo.FileStream,
                        IsEndNUmber = false,
                        Number = 1
                    });
                }

                if (this.FileInfo.UploadState == 0)
                {
                    this.FileInfo = new UploadInfo
                    {
                        User = uploadInfo.User,
                        Role = uploadInfo.Role,
                        FileStream = uploadInfo.FileStream,
                        CreateDate = uploadInfo.CreateDate,
                        SuffixName = uploadInfo.SuffixName,
                        TotalLength = uploadInfo.TotalLength == 0 ? uploadInfo.FileStream.Length : uploadInfo.TotalLength,
                        UploadState = 1,
                        ContentType = uploadInfo.ContentType
                    };
                }
            }
        }

        public async Task UploadComplatedAsync()
        {
            if (UploadPieceTransaction != null)
            {
                try
                {
                    UploadPieceTransaction.Commit();
                    UploadPieceTransaction.Dispose();
                    UploadPieceConnection?.Close();
                    UploadPieceConnection?.Dispose();
                }
                finally
                {
                    UploadPieceTransaction = null;
                    UploadPieceConnection = null;
                }
            }
            else
            {
                throw new Exception("state error");
            }

            using (var connectionOfPiece = new SqliteConnection(this.connectionStringOfPiece))
            {
                connectionOfPiece.Open();
                var selectCommand = connectionOfPiece.CreateCommand();
                selectCommand.CommandText = @"select Number,IsEndNUmber,PieceLength from FilePieceList where key=$key order by Number";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);

                List<UploadPiece> pieces = new List<UploadPiece>();
                List<long> piecesLengths = new List<long>();
                using (var reader = selectCommand.ExecuteReader())
                {
                    while (reader.Read())
                    {
                        piecesLengths.Add(reader.GetInt64(2));
                        pieces.Add(new UploadPiece
                        {
                            Number = reader.GetInt32(0),
                            IsEndNUmber = reader.GetInt16(1) == 1
                        });
                    }
                }

                // 处理分片
                for (int i = 0; i < pieces.Count; i++)
                {
                    if (pieces[i].Number != (i+1))
                    {
                        throw new Exception(string.Format("No data slice found index：{0}", i + 1));
                    }

                    if (pieces[i].IsEndNUmber)
                    {
                        using (var connection = new SqliteConnection(this.connectionString))
                        {
                            connection.Open();
                            var insertCommand = connection.CreateCommand();
                            insertCommand.CommandText = @"update FileList set UploadState=201,TotalLength=$TotalLength where key=$key";
                            insertCommand.Parameters.AddWithValue("$key", this.FileID);
                            insertCommand.Parameters.AddWithValue("$TotalLength", piecesLengths.Sum());
                            insertCommand.ExecuteNonQuery();
                        }


                        this.FileInfo = new UploadInfo
                        {
                            CreateDate = this.FileInfo.CreateDate,
                            //FileStream= this.FileInfo.FileStream,
                            UploadState = 201,
                            TotalLength = (int)piecesLengths.Sum(),
                            Role = this.FileInfo.Role,
                            SuffixName = this.FileInfo.SuffixName,
                            User = this.FileInfo.User,
                            ContentType= this.FileInfo.ContentType,
                        };

                        break;
                    }
                }
            }

            // 同步通知
            await this.NotifySyncFileDataAsync();

        }

        SqliteConnection? UploadPieceConnection = null;
        SqliteTransaction? UploadPieceTransaction = null;
        public async Task UploadPieceAsync(UploadPiece uploadInfo)
        {
            if (UploadPieceConnection == null)
            {
                UploadPieceConnection = new SqliteConnection(this.connectionStringOfPiece);
            }
            UploadPieceConnection.Open();
            if (UploadPieceTransaction == null)
            {
                UploadPieceTransaction = UploadPieceConnection.BeginTransaction(IsolationLevel.ReadUncommitted);
            }

            var insertCommand = UploadPieceConnection.CreateCommand();
            insertCommand.CommandText =
            @"
                    INSERT INTO FilePieceList(key, Number,IsEndNUmber,PieceLength,FileStream,CreateDate)
                    VALUES ($key, $Number,$IsEndNUmber,$PieceLength,$FileStream,$CreateDate);
                ";

            insertCommand.Parameters.AddWithValue("$key", this.FileID);
            insertCommand.Parameters.AddWithValue("$Number", uploadInfo.Number);
            insertCommand.Parameters.AddWithValue("$IsEndNUmber", uploadInfo.IsEndNUmber ? 1 : 0);
            insertCommand.Parameters.AddWithValue("$PieceLength", uploadInfo.FileStream.LongLength);
            insertCommand.Parameters.AddWithValue("$FileStream", uploadInfo.FileStream);
            insertCommand.Parameters.AddWithValue("$CreateDate", DateTime.Now);

            try
            {
                insertCommand.ExecuteNonQuery();
            }
            catch (Exception)
            {
                UploadPieceTransaction.Rollback();
                UploadPieceTransaction.Dispose();
                UploadPieceConnection?.Close();
                UploadPieceConnection?.Dispose();
                UploadPieceTransaction = null;
                UploadPieceConnection = null;
                throw;
            }
        }

        //public async Task UploadPieceAsync(UploadPiece uploadInfo)
        //{
        //    using (var connection = new SqliteConnection(this.connectionStringOfPiece))
        //    {
        //        connection.Open();
        //        var insertCommand = connection.CreateCommand();
        //        insertCommand.CommandText =
        //        @"
        //            INSERT INTO FilePieceList(key, Number,IsEndNUmber,PieceLength,FileStream,CreateDate)
        //            VALUES ($key, $Number,$IsEndNUmber,$PieceLength,zeroblob($PieceLength),$CreateDate);
        //            SELECT last_insert_rowid();
        //        ";

        //        insertCommand.Parameters.AddWithValue("$key", this.FileID);
        //        insertCommand.Parameters.AddWithValue("$Number", uploadInfo.Number);
        //        insertCommand.Parameters.AddWithValue("$IsEndNUmber", uploadInfo.IsEndNUmber ? 1 : 0);
        //        insertCommand.Parameters.AddWithValue("$PieceLength", uploadInfo.FileStream.LongLength);
        //        insertCommand.Parameters.AddWithValue("$CreateDate", DateTime.Now);

        //        var rowid = (long)insertCommand.ExecuteScalar();

        //        var writeStream = new SqliteBlob(connection, "FilePieceList", "FileStream", rowid);
        //        new MemoryStream(uploadInfo.FileStream).CopyTo(writeStream);
        //        writeStream.Flush();
        //        writeStream.Dispose();
        //    }
        //}

        public async Task<bool> IsExistsAsync() 
        {
            if (this.FileInfo.UploadState > 0)
            {
                return true;
            }
            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"select User,Role,SuffixName,TotalLength,CreateDate,FileStream,UploadState,ContentType from FileList where key=$key";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);
                var reader = selectCommand.ExecuteReader();
                while (reader.Read()) 
                {
                    var outputStream = new MemoryStream();
                    using (var readStream = reader.GetStream(5))
                    {
                        await readStream.CopyToAsync(outputStream);
                    }

                    this.FileInfo = new UploadInfo
                    {
                        User = reader.GetString(0),
                        Role = reader.GetString(1),
                        SuffixName = reader.GetString(2),
                        TotalLength = reader.GetInt32(3),
                        CreateDate = DateTime.Parse(reader.GetString(4)),
                        FileStream = outputStream.ToArray(),
                        UploadState = reader.GetInt32(6),
                        ContentType = reader.GetString(7),
                    };

                    return this.FileInfo.UploadState >= 200;
                }
            }
            return false;
        }

        public async Task DeleteFileAsync()
        {
            //if (this.FileInfo.UploadState == 0)
            //{
            //    return Task.CompletedTask;
            //}

            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"delete from FileList where key=$key";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);
                selectCommand.ExecuteNonQuery();
            }

            using (var connection = new SqliteConnection(this.connectionStringOfPiece))
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"delete from FilePieceList where key=$key";
                selectCommand.Parameters.AddWithValue("$key", this.FileID);
                selectCommand.ExecuteNonQuery();
            }

            // 通知其它Service删除文件
            await this.NotifyDeleteFileDataAsync();

            base.DeactivateOnIdle();
        }

        public Task<List<UploadInfo>> QueryFileInfoAsync(string query)
        {
            if (query.StartsWith("where "))
            {
                query = query.Substring(5);
            }

            List<UploadInfo> list = new List<UploadInfo>();
            using (var connection = new SqliteConnection(this.connectionString))
            {
                connection.Open();
                var selectCommand = connection.CreateCommand();
                selectCommand.CommandText = @"select User,Role,SuffixName,TotalLength,CreateDate,UploadState,ContentType,key from FileList where " + query;
                var reader = selectCommand.ExecuteReader();
                while (reader.Read())
                {
                    list.Add(new UploadInfo
                    {
                        User = reader.GetString(0),
                        Role = reader.GetString(1),
                        SuffixName = reader.GetString(2),
                        TotalLength = reader.GetInt32(3),
                        CreateDate = DateTime.Parse(reader.GetString(4)),
                        UploadState = reader.GetInt32(5),
                        ContentType = reader.GetString(6),
                        Key = reader.GetString(7),
                    });
                }
            }
            return Task.FromResult(list);
        }

        public async Task<byte[]> GetStreamAsync(int width, int height)
        {
            MemoryStream fileStream = new MemoryStream();
            int page = 0;
            while (true)
            {
                page++;
                var file = await this.GetStreamAsync(page);
                await fileStream.WriteAsync(file.FileStream, 0, file.FileStream.Length);
                if (file.IsEndNUmber)
                {
                    break;
                }
            }

            var bitmap = RetrunBitmap(System.Drawing.Image.FromStream(fileStream), width, height);
            // 重置图片宽高
            //var bitmap = new Bitmap(Image.FromStream(fileStream), width, height);

            fileStream.Dispose();
            fileStream = new MemoryStream();
            if (this.FileInfo.SuffixName.IndexOf("gif") > -1)
            {
                bitmap.Save(fileStream, ImageFormat.Gif);
            }
            else if (this.FileInfo.SuffixName.IndexOf("png") > -1)
            {
                bitmap.Save(fileStream, ImageFormat.Png);
            }
            else
            {
                bitmap.Save(fileStream, ImageFormat.Jpeg);
            }
            bitmap.Dispose();

            return fileStream.ToArray();
        }

        public async Task CopyFileAsync(string siloAddress)
        {
            var concurrentFileInfo = await this.GetFileInfoAsync();

            if (concurrentFileInfo.UploadState > 0)
            {
                // 同步或任务已经存在
                return;
            }

            var service = this.GrainFactory.GetGrain<iFileService>(string.Format("{0}/{1}", siloAddress, this.FileID));
            var fileInfo = await service.GetFileInfoAsync();

            if (fileInfo.UploadState >= 200)
            {
                var fileData = await service.GetStreamAsync();
                if (fileData.IsEndNUmber)
                {
                    fileInfo.FileStream = fileData.FileStream;
                }

                // 上传到自己服务
                await this.UploadAsync(fileInfo);

                // 不需要分片就结束
                if (fileData.IsEndNUmber)
                {
                    return;
                }

                // 获取分片数据
                await this.UploadPieceAsync(fileData);// 这是第一页

                int page = 1;
                while (true)
                {
                    page++;
                    fileData = await service.GetStreamAsync(page); // 从第二页开始拉数据
                    await this.UploadPieceAsync(fileData); // 把数据写入到当前服务器

                    // 数据获取结束
                    if (fileData.IsEndNUmber)
                    {
                        // 写入成功
                        await this.UploadComplatedAsync();
                        break;
                    }
                }
            }
            else
            {
                // 文件上传 还没有完成，则暂时放弃同步
            }
        }


        private Bitmap RetrunBitmap(System.Drawing.Image originalImage, int width, int height)
        {
            int towidth = width;
            int toheight = height;

            int x = 0;
            int y = 0;
            int ow = originalImage.Width;
            int oh = originalImage.Height;

            if ((double)originalImage.Width / (double)originalImage.Height > (double)towidth / (double)toheight)
            {
                oh = originalImage.Height;
                ow = originalImage.Height * towidth / toheight;
                y = 0;
                x = (originalImage.Width - ow) / 2;
            }
            else
            {
                ow = originalImage.Width;
                oh = originalImage.Width * height / towidth;
                x = 0;
                y = (originalImage.Height - oh) / 2;
            }

            Bitmap bitmap = new Bitmap(towidth, toheight);
            Graphics g = Graphics.FromImage(bitmap);
            g.InterpolationMode = InterpolationMode.High;
            g.SmoothingMode = SmoothingMode.HighQuality;
            g.Clear(Color.Transparent);
            g.DrawImage(originalImage, new Rectangle(0, 0, towidth, toheight), new Rectangle(x, y, ow, oh), GraphicsUnit.Pixel);

            // 水印
            if (false)
            {
                //填充半透明三角形
                var triggleLeg = (bitmap.Width > bitmap.Height ? bitmap.Height : bitmap.Width) / 5;
                var path = new GraphicsPath();
                path.AddLine(bitmap.Width - triggleLeg, bitmap.Height, bitmap.Width, bitmap.Height - triggleLeg);
                path.AddLine(bitmap.Width, bitmap.Height - triggleLeg, bitmap.Width, bitmap.Height);
                path.AddLine(bitmap.Width, bitmap.Height, bitmap.Width - triggleLeg, bitmap.Height);

                // 透明度 0 - 255
                g.FillPath(new SolidBrush(Color.FromArgb(16, Color.Black)), path);

                //将原点移动到三角形斜边的中间位置
                g.TranslateTransform(bitmap.Width - triggleLeg / 2, bitmap.Height - triggleLeg / 2);
                //旋转45度
                g.RotateTransform(-45);

                var fontSize = Math.Ceiling((decimal)bitmap.Width / 100 * 3);
                //绘制水印文字
                var font = new Font("Arial", (int)fontSize, FontStyle.Bold);
                string text = "iTool";
                //测量文字长度
                var size = g.MeasureString(text, font);
                Brush whiteBrush = new SolidBrush(Color.FromArgb(120, 255, 255, 255));
                //绘制文字时，以文字长度的中间位置为中心，因此绘制的起点为：0-长度/2；并设置高度距离原点半行高
                g.DrawString(text, font, whiteBrush, -size.Width / 2, size.Height / 2);
                //size = g.MeasureString(DateTime.Now.ToString("yyyy-MM-dd"), font);
                //g.DrawString(DateTime.Now.ToString("yyyy-MM-dd"), font, Brushes.White, -size.Width / 2, size.Height * 3 / 2);
            }

            try
            {
                return bitmap;
            }
            catch (System.Exception e)
            {
                throw e;
            }
            finally
            {
                originalImage.Dispose();
                g.Dispose();
            }
        }

        private async Task NotifySyncFileDataAsync()
        {
            if (this.isHasRoute)
            {
                // 同步发起节点不带Route
                return;
            }
            var management = this.GrainFactory.GetGrain<IManagementGrain>(0);
            var result = await management.GetHosts(true);
            var noders = result
                .Where(item => !item.Key.Equals(this.siloStatusOracle.SiloAddress))
                .Select(item => this.GrainFactory.GetGrain<iFileExistsService>(string.Format("{0}/{1}", item.Key.ToParsableString(), this.FileID)));

            await Task.WhenAll(noders.Select(item => item.AcceptNotificationSyncFileDataAsync(this.siloStatusOracle.SiloAddress.ToParsableString())));
        }

        private async Task NotifyDeleteFileDataAsync()
        {
            if (this.isHasRoute)
            {
                // 同步发起节点不带Route
                return;
            }
            var management = this.GrainFactory.GetGrain<IManagementGrain>(0);
            var result = await management.GetHosts(true);
            var noders = result
                .Where(item => !item.Key.Equals(this.siloStatusOracle.SiloAddress))
                .Select(item => this.GrainFactory.GetGrain<iFileExistsService>(string.Format("{0}/{1}", item.Key.ToParsableString(), this.FileID)));

            await Task.WhenAll(noders.Select(item => item.AcceptNotificationDaleteFileDataAsync()));
        }
    }
}
